"use strict";

var $ = require("jquery"),
    translator = require("animation/translator"),
    animationFrame = require("animation/frame"),
    support = require("core/utils/support"),
    browser = require("core/utils/browser"),
    devices = require("core/devices"),
    config = require("core/config"),
    messageLocalization = require("localization/message"),
    Scrollbar = require("ui/scroll_view/ui.scrollbar"),
    pointerMock = require("../../helpers/pointerMock.js");

require("common.css!");
require("ui/scroll_view");

var SCROLLVIEW_CLASS = "dx-scrollview",
    SCROLLABLE_CONTENT_CLASS = "dx-scrollable-content",
    SCROLLABLE_CONTAINER_CLASS = "dx-scrollable-container",
    SCROLLABLE_SCROLL_CLASS = "dx-scrollable-scroll",
    SCROLLABLE_SCROLLBAR_CLASS = "dx-scrollable-scrollbar",
    SCROLLVIEW_CONTENT_CLASS = "dx-scrollview-content",
    SCROLLVIEW_TOP_POCKET_CLASS = "dx-scrollview-top-pocket",
    SCROLLVIEW_BOTTOM_POCKET_CLASS = "dx-scrollview-bottom-pocket",
    SCROLLVIEW_LOADPANEL = "dx-scrollview-loadpanel",
    SCROLLABLE_WRAPPER_CLASS = "dx-scrollable-wrapper",

    SCROLLVIEW_PULLDOWN_CLASS = SCROLLVIEW_CLASS + "-pull-down",
    SCROLLVIEW_PULLDOWN_IMAGE_CLASS = SCROLLVIEW_PULLDOWN_CLASS + "-image",
    SCROLLVIEW_PULLDOWN_TEXT_CLASS = SCROLLVIEW_PULLDOWN_CLASS + "-text",
    SCROLLVIEW_PULLDOWN_LOADING_CLASS = SCROLLVIEW_PULLDOWN_CLASS + "-loading",
    SCROLLVIEW_PULLDOWN_READY_CLASS = SCROLLVIEW_PULLDOWN_CLASS + "-ready",
    SCROLLVIEW_PULLDOWN_INDICATOR_CLASS = SCROLLVIEW_PULLDOWN_CLASS + "-indicator",

    SCROLLVIEW_REACHBOTTOM_CLASS = SCROLLVIEW_CLASS + "-scrollbottom",
    SCROLLVIEW_REACHBOTTOM_TEXT_CLASS = SCROLLVIEW_REACHBOTTOM_CLASS + "-text",
    SCROLLVIEW_REACHBOTTOM_INDICATOR_CLASS = SCROLLVIEW_REACHBOTTOM_CLASS + "-indicator",

    PULLDOWN_HEIGHT = 160;

var getScrollOffset = function($scrollView) {
    var $content = $scrollView.find('.' + SCROLLABLE_CONTENT_CLASS),
        $container = $scrollView.find('.' + SCROLLABLE_CONTAINER_CLASS),
        location = translator.locate($content);

    return {
        top: (location.top - $container.scrollTop() || 0),
        left: (location.left || -$container.scrollLeft() || 0)
    };
};

devices.current("iPhone");

var moduleConfig = {
    beforeEach: function() {
        this.clock = sinon.useFakeTimers();
        this._originalRequestAnimationFrame = animationFrame.requestAnimationFrame;
        animationFrame.requestAnimationFrame = function(callback) {
            callback();
        };
    },
    afterEach: function() {
        this.clock.restore();
        animationFrame.requestAnimationFrame = this._originalRequestAnimationFrame;
    }
};


QUnit.testStart(function() {
    var markup = '\
        <div id="scrollView" style="height: 50px; width: 50px;">\
            <div class="content1" style="height: 100px; width: 100px;"></div>\
            <div class="content2" style="height: 100px; width: 100px;"></div>\
        </div>';

    $("#qunit-fixture").html(markup);
});

QUnit.module("render", moduleConfig);

QUnit.test("scrollView render", function(assert) {
    var $scrollView = $("#scrollView").dxScrollView({ useNative: false }),
        $scrollableContent = $scrollView.find("." + SCROLLABLE_CONTENT_CLASS),
        $topPocket = $scrollableContent.children().eq(0),
        $content = $scrollableContent.children().eq(1),
        $bottomPocket = $scrollableContent.children().eq(2);

    assert.ok($scrollView.hasClass(SCROLLVIEW_CLASS), "dx-scrollview class attached");
    assert.ok($topPocket.hasClass(SCROLLVIEW_TOP_POCKET_CLASS), "dx-scrollview-top-pocket class attached");
    assert.ok($content.hasClass(SCROLLVIEW_CONTENT_CLASS), "dx-scrollview-content class attached");
    assert.ok($bottomPocket.hasClass(SCROLLVIEW_BOTTOM_POCKET_CLASS), "dx-scrollview-bottom-pocket");

    assert.equal($content.children().length, 2, "content was moved");
    assert.ok($content.children().eq(0).hasClass("content1"));
    assert.ok($content.children().eq(1).hasClass("content2"));
});

QUnit.test("scrollView pullDown markup", function(assert) {
    var $scrollView = $("#scrollView").dxScrollView({ useNative: false }),
        $pullDown = $scrollView.find("." + SCROLLVIEW_PULLDOWN_CLASS);

    assert.equal($pullDown.length, 1, "pull down container");
    assert.equal($pullDown.find("." + SCROLLVIEW_PULLDOWN_IMAGE_CLASS).length, 1, "pull down image");
    assert.equal($pullDown.find("." + SCROLLVIEW_PULLDOWN_INDICATOR_CLASS).length, 1, "pull down load indicator container");
    assert.equal($pullDown.find("." + SCROLLVIEW_PULLDOWN_TEXT_CLASS).length, 1, "pull down text");
});

QUnit.test("dxLoadIndicator was created", function(assert) {
    var $scrollView = $("#scrollView").dxScrollView({ useNative: false }),
        $topPocket = $scrollView.find("." + SCROLLVIEW_TOP_POCKET_CLASS),
        $bottomPocket = $scrollView.find("." + SCROLLVIEW_BOTTOM_POCKET_CLASS),
        $loadIndicatorTop = $topPocket.find(".dx-loadindicator"),
        $loadIndicatorBottom = $bottomPocket.find(".dx-loadindicator");

    var top = $loadIndicatorTop.data("dxLoadIndicator"),
        bottom = $loadIndicatorBottom.data("dxLoadIndicator");

    assert.notEqual(top, null, "dxLoadIndicator was created");
    assert.notEqual(bottom, null, "dxLoadIndicator was created");
});

QUnit.test("pulldown text", function(assert) {
    var $scrollView = $("#scrollView").dxScrollView({
            onPullDown: $.noop,
            useNative: false
        }),
        pullDownText = $scrollView.find("." + SCROLLVIEW_PULLDOWN_TEXT_CLASS),
        scrollBottomText = $scrollView.find("." + SCROLLVIEW_REACHBOTTOM_TEXT_CLASS);

    assert.equal(pullDownText.children().eq(0).text(), messageLocalization.format("dxScrollView-pullingDownText"));
    assert.equal(pullDownText.children().eq(1).text(), messageLocalization.format("dxScrollView-pulledDownText"));
    assert.equal(pullDownText.children().eq(2).text(), messageLocalization.format("dxScrollView-refreshingText"));
    assert.equal(scrollBottomText.text(), "Loading...");
});

QUnit.test("scrollView scrollbottom markup", function(assert) {
    var $scrollView = $("#scrollView").dxScrollView({ useNative: false }),
        $bottomPocket = $scrollView.find("." + SCROLLVIEW_BOTTOM_POCKET_CLASS),
        $scrollBottom = $bottomPocket.find("." + SCROLLVIEW_REACHBOTTOM_CLASS);

    assert.equal($scrollBottom.length, 1, "scroll bottom container");
    assert.equal($scrollBottom.find("." + SCROLLVIEW_REACHBOTTOM_TEXT_CLASS).length, 1, "scrollbottom text");
    assert.equal($scrollBottom.find("." + SCROLLVIEW_REACHBOTTOM_INDICATOR_CLASS).length, 1, "scrollbottom load indicator container");
});

QUnit.test("scrollView has correct height when container height is auto", function(assert) {
    var $scrollView = $("#scrollView");

    $scrollView.height("auto");
    $scrollView.dxScrollView({ useNative: false, pushBackValue: 0 });

    var $content = $scrollView.find("." + SCROLLVIEW_CONTENT_CLASS);

    assert.equal($scrollView.height(), $content.height(), "scrollView has correctly height");
});

QUnit.test("pulling down text change", function(assert) {
    var $scrollView = $("#scrollView").dxScrollView({ useNative: false });

    $scrollView.dxScrollView("option", "pullingDownText", "test");

    var $pullingDownText = $scrollView.find("." + SCROLLVIEW_PULLDOWN_TEXT_CLASS).children().eq(0);
    assert.equal($pullingDownText.text(), "test");
});

QUnit.test("pulled down text change", function(assert) {
    var $scrollView = $("#scrollView").dxScrollView({ useNative: false });
    $scrollView.dxScrollView("option", "pulledDownText", "test");

    var $pullingDownText = $scrollView.find("." + SCROLLVIEW_PULLDOWN_TEXT_CLASS).children().eq(1);
    assert.equal($pullingDownText.text(), "test");
});

QUnit.test("refreshing text change", function(assert) {
    var $scrollView = $("#scrollView").dxScrollView({ useNative: false });
    $scrollView.dxScrollView("option", "refreshingText", "test");

    var $pullingDownText = $scrollView.find("." + SCROLLVIEW_PULLDOWN_TEXT_CLASS).children().eq(2);
    assert.equal($pullingDownText.text(), "test");
});

QUnit.test("reachbottom text change", function(assert) {
    var $scrollView = $("#scrollView").dxScrollView({ useNative: false });
    $scrollView.dxScrollView("option", "reachBottomText", "test");

    var $scrollBottomText = $("." + SCROLLVIEW_REACHBOTTOM_TEXT_CLASS);
    assert.equal($scrollBottomText.text(), "test");
});

QUnit.module("dimension", moduleConfig);

QUnit.test("top position calculation", function(assert) {
    var $scrollView = $("#scrollView").dxScrollView({
            useNative: false,
            onPullDown: $.noop
        }),
        $topPocket = $scrollView.find("." + SCROLLVIEW_TOP_POCKET_CLASS);

    var location = getScrollOffset($scrollView);
    assert.equal(location.top, -$topPocket.height(), "top position calculated considering top pocket height");
});

QUnit.test("bottom position calculation", function(assert) {
    var $scrollView = $("#scrollView").dxScrollView({
        useNative: false,
        inertiaEnabled: false,
        onEnd: function() {
            var location = getScrollOffset($scrollView),
                height = $container.height() - $content.height() + $bottomPocket.height();
            assert.equal(location.top, height, "bottom position calculated considering top pocket height");
        }
    });

    var $container = $scrollView.find(".dx-scrollable-container"),
        $content = $scrollView.find("." + SCROLLABLE_CONTENT_CLASS),
        $bottomPocket = $scrollView.find("." + SCROLLVIEW_BOTTOM_POCKET_CLASS),
        $topPocket = $scrollView.find("." + SCROLLVIEW_TOP_POCKET_CLASS);

    pointerMock($content)
        .start()
        .down()
        .move(0, $container.height() - $content.height() + $topPocket.height() + $bottomPocket.height() - 10)
        .up();
});


QUnit.module("actions", moduleConfig);

QUnit.test("onPullDown action", function(assert) {
    var firstScroll = true,
        offset = 10,
        $scrollView = $("#scrollView").dxScrollView({
            useNative: false,
            inertiaEnabled: false,
            onPullDown: function(e) {
                assert.ok(true, "pulldown action was fired");
            },
            onScroll: function() {
                if(firstScroll) {
                    var location = getScrollOffset($scrollView);
                    assert.equal(location.top, offset);
                }
                firstScroll = false;
            },
            onEnd: function() {
                assert.ok(false, "end action should not be fired");
            }
        });
    var $content = $scrollView.find("." + SCROLLABLE_CONTENT_CLASS),
        $topPocket = $scrollView.find("." + SCROLLVIEW_TOP_POCKET_CLASS);

    pointerMock($content)
        .start()
        .down()
        .move(0, $topPocket.height() + offset)
        .up();
});

QUnit.test("no onPullDown hides pullDown", function(assert) {
    if(devices.real().platform === "win") { // NOTE: pulldown is hidden always for win8
        assert.expect(0);
        return;
    }
    var $scrollView = $("#scrollView").dxScrollView({});
    assert.ok($scrollView.find("." + SCROLLVIEW_PULLDOWN_CLASS).is(":hidden"), "pull down element is hidden");
});

QUnit.test("changing of onPullDown option changes pullDown visibility", function(assert) {
    if(devices.real().platform === "win") { // NOTE: pulldown is hidden always for win8
        assert.expect(0);
        return;
    }
    var $scrollView = $("#scrollView").dxScrollView({});
    $scrollView.dxScrollView("option", "onPullDown", $.noop);
    assert.ok($scrollView.find("." + SCROLLVIEW_PULLDOWN_CLASS).is(":visible"), "pull down element is visible");
});

QUnit.test("pullDown behavior turns off when pullDown is undefined", function(assert) {
    var $scrollView = $("#scrollView").dxScrollView({
        useNative: false,
        inertiaEnabled: false,
        onEnd: function() {
            assert.ok(true, "end action should be fired");
        }
    });
    var $content = $scrollView.find("." + SCROLLABLE_CONTENT_CLASS),
        $topPocket = $scrollView.find("." + SCROLLVIEW_TOP_POCKET_CLASS);

    pointerMock($content)
        .start()
        .down()
        .move(0, $topPocket.height() + 10)
        .up();
});

QUnit.test("top position calculation on pull down", function(assert) {
    var $scrollView = $("#scrollView").dxScrollView({
            useNative: false,
            onPullDown: $.noop
        }),
        $content = $scrollView.find('.' + SCROLLABLE_CONTENT_CLASS),
        offset = 2;

    pointerMock($content)
        .start()
        .down()
        .move(0, offset);

    var location = translator.locate($content),
        $container = $scrollView.find('.' + SCROLLABLE_CONTAINER_CLASS),
        $topPocket = $scrollView.find("." + SCROLLVIEW_TOP_POCKET_CLASS);

    assert.equal(location.top, 0, "translate top position is right");
    assert.equal($container.scrollTop(), $topPocket.height() - offset, "scroll top position is right");

});

QUnit.test("onReachBottom action", function(assert) {
    var $scrollView = $("#scrollView").dxScrollView({
        useNative: false,
        inertiaEnabled: false,
        onReachBottom: function(e) {
            assert.ok(true, "onReachBottom action was fired");
        },
        onEnd: function() {
            assert.ok(false, "end action should not be fired");
        }
    });
    var $container = $scrollView.find(".dx-scrollable-container"),
        $content = $scrollView.find("." + SCROLLABLE_CONTENT_CLASS),
        $bottomPocket = $scrollView.find("." + SCROLLVIEW_BOTTOM_POCKET_CLASS);

    pointerMock($content)
        .start()
        .down()
        .move(0, $container.height() - $content.height() - $bottomPocket.height() - 1)
        .up();
});

QUnit.test("onReachBottom action should not be fired in rtl mode", function(assert) {
    assert.expect(0);

    var $scrollView = $("#scrollView").dxScrollView({
        useNative: false,
        direction: "vertical",
        rtlEnabled: false,
        onReachBottom: function(e) {
            assert.ok(false, "onReachBottom action won't fired");
        }
    });

    var $content = $scrollView.find("." + SCROLLABLE_CONTENT_CLASS);
    $content.width(200);
    $content.height(0);

    $scrollView.dxScrollView("instance").option("rtlEnabled", true);
});

QUnit.test("changing of onReachBottom option changes reach bottom element visibility", function(assert) {
    var $scrollView = $("#scrollView").dxScrollView({ useNative: false });
    $scrollView.dxScrollView("option", "onReachBottom", $.noop);
    assert.ok($scrollView.find("." + SCROLLVIEW_REACHBOTTOM_CLASS).is(":visible"), "reach bottom element is visible");
});

QUnit.test("onReachBottom action fired once", function(assert) {
    assert.expect(1);

    var reachBottomFired = 0;

    var $scrollView = $("#scrollView").dxScrollView({
        useNative: false,
        inertiaEnabled: false,
        onReachBottom: function(e) {
            reachBottomFired++;
            assert.equal(reachBottomFired, 1, "onReachBottom fired once");
        }
    });

    var $content = $scrollView.find("." + SCROLLABLE_CONTENT_CLASS);

    pointerMock($content)
        .start()
        .down()
        .move(0, -$content.height())
        .move(0, -1)
        .up();
});

QUnit.test("changing action option does not cause render", function(assert) {
    var $scrollView = $("#scrollView").dxScrollView({
        useNative: false
    });

    var $content = $scrollView.find("." + SCROLLABLE_CONTENT_CLASS),
        $topPocket = $scrollView.find("." + SCROLLVIEW_TOP_POCKET_CLASS),
        mouse = pointerMock($content).start();

    mouse
        .down()
        .move(0, -10);

    var testAction = function(actionName) {
        $scrollView.dxScrollView("option", actionName, $.noop);
        var location = getScrollOffset($scrollView);
        assert.equal(location.top, -$topPocket.height() - 10, actionName + " case scrollable rerendered");
    };

    testAction("onPullDown");
    testAction("onReachBottom");
});

QUnit.test("onReachBottom action is not fired when scrollable content bottom is not reached", function(assert) {
    var $scrollView = $("#scrollView").dxScrollView({
        useNative: false,
        inertiaEnabled: false,
        onReachBottom: function(e) {
            assert.ok(false, "onReachBottom action should not be fired");
        },
        onEnd: function() {
            assert.ok(true, "end action was fired");
        }
    });
    var $container = $scrollView.find(".dx-scrollable-container"),
        $content = $scrollView.find("." + SCROLLVIEW_CONTENT_CLASS),
        $bottomPocket = $scrollView.find("." + SCROLLVIEW_BOTTOM_POCKET_CLASS);

    pointerMock($content)
        .start()
        .down()
        .move(0, $container.height() - ($content.height() - $bottomPocket.outerHeight() + 1))
        .up();
});

QUnit.test("disabled scrollview should not be updated on pointerdown after finish loading", function(assert) {
    var count = 0;
    var $scrollView = $("#scrollView").dxScrollView({
        pushBackValue: 1,
        onUpdated: function() {
            count++;
        },
        useNative: true,
        disabled: true
    });

    $scrollView.dxScrollView("finishLoading");
    pointerMock($scrollView.find(".content1")).start().down();

    assert.equal(count, 1, "update action won't fired");
});


QUnit.module("dynamic", moduleConfig);

QUnit.test("pulling down", function(assert) {
    assert.expect(1);

    var $scrollView = $("#scrollView").dxScrollView({
        useNative: false,
        inertiaEnabled: false,
        onPullDown: function() {
            var location = getScrollOffset($scrollView);
            assert.equal(location.top, 0, "pulled down");
        }
    });
    var $content = $scrollView.find("." + SCROLLABLE_CONTENT_CLASS),
        $topPocket = $scrollView.find("." + SCROLLVIEW_TOP_POCKET_CLASS);

    pointerMock($content)
        .start()
        .down()
        .move(0, $topPocket.height() + 1)
        .up();
});

QUnit.test("pulling down without release", function(assert) {
    assert.expect(1);

    var $scrollView = $("#scrollView").dxScrollView({
        useNative: false,
        onPullDown: $.noop,
        inertiaEnabled: false,
        onEnd: function() {
            var location = getScrollOffset($scrollView);
            assert.equal(location.top, -$topPocket.height(), "content bounced to the top");
        }
    });
    var $content = $scrollView.find("." + SCROLLABLE_CONTENT_CLASS),
        $topPocket = $scrollView.find("." + SCROLLVIEW_TOP_POCKET_CLASS);

    pointerMock($content)
        .start()
        .down()
        .move(0, $topPocket.height() + 1)
        .move(0, -10)
        .up();
});

QUnit.test("onPullDown enabled doesn't change the position of content", function(assert) {
    assert.expect(1);

    var $scrollView = $("#scrollView").dxScrollView({
        useNative: false,
        inertiaEnabled: false
    });

    var $content = $scrollView.find("." + SCROLLABLE_CONTENT_CLASS),
        $topPocket = $scrollView.find("." + SCROLLVIEW_TOP_POCKET_CLASS);

    pointerMock($content)
        .start()
        .down()
        .move(0, -10)
        .up();

    $scrollView.dxScrollView("option", "onPullDown", $.noop);

    var location = getScrollOffset($scrollView);

    assert.equal(location.top, -10 - $topPocket.height(), "content position was not changed");
});

QUnit.test("onPullDown disabled does not change the position of content", function(assert) {
    assert.expect(1);

    var $scrollView = $("#scrollView").dxScrollView({
        useNative: false,
        inertiaEnabled: false,
        onPullDown: $.noop
    });

    var $content = $scrollView.find("." + SCROLLABLE_CONTENT_CLASS);

    pointerMock($content)
        .start()
        .down()
        .move(0, -10)
        .up();

    $scrollView.dxScrollView("option", "onPullDown", undefined);

    var location = getScrollOffset($scrollView);

    assert.equal(location.top, -10, "content position was not changed");
});

QUnit.test("scroll content stays in bounds when onPullDown turned off", function(assert) {
    assert.expect(1);

    var $scrollView = $("#scrollView").dxScrollView({
        useNative: false,
        inertiaEnabled: false,
        onPullDown: $.noop,
        onReachBottom: $.noop
    });

    var $container = $scrollView.find("." + SCROLLABLE_CONTAINER_CLASS),
        $content = $scrollView.find("." + SCROLLVIEW_CONTENT_CLASS);

    pointerMock($content)
        .start()
        .down()
        .move(0, $container.height() - $content.height())
        .up();

    $scrollView.dxScrollView("option", "onPullDown", null);

    var location = getScrollOffset($scrollView);

    assert.equal(location.top, $container.height() - $content.height(), "content position was not changed");
});

QUnit.test("pulled down adds ready state", function(assert) {
    assert.expect(4);

    var $scrollView = $("#scrollView").dxScrollView({
        useNative: false,
        inertiaEnabled: false,
        onPullDown: $.noop
    });
    var $content = $scrollView.find("." + SCROLLABLE_CONTENT_CLASS),
        $topPocket = $scrollView.find("." + SCROLLVIEW_TOP_POCKET_CLASS),
        $pullDownText = $scrollView.find("." + SCROLLVIEW_PULLDOWN_TEXT_CLASS),
        mouse = pointerMock($content).start();

    mouse.down().move(0, $topPocket.height() + 1);
    assert.equal($topPocket.children().eq(0).hasClass(SCROLLVIEW_PULLDOWN_READY_CLASS), true, "scrollview-pull-down-ready class added");
    assert.equal($pullDownText.children().eq(1).css("opacity"), 1);

    mouse.move(0, -10);
    assert.equal($topPocket.children().eq(0).hasClass(SCROLLVIEW_PULLDOWN_READY_CLASS), false, "scrollview-pull-down-ready class removed");
    assert.equal($pullDownText.children().eq(0).css("opacity"), 1);
});

QUnit.test("pulled down adds loading state", function(assert) {
    assert.expect(3);

    var $scrollView = $("#scrollView").dxScrollView({
        useNative: false,
        inertiaEnabled: false,
        onPullDown: function() {
            assert.equal($topPocket.children().eq(0).hasClass(SCROLLVIEW_PULLDOWN_LOADING_CLASS), true, "scrollview-pull-down-loading class added");
            assert.equal($topPocket.children().eq(0).hasClass(SCROLLVIEW_PULLDOWN_READY_CLASS), false, "scrollview-pull-down-ready class removed");
            assert.equal($pullDownText.children().eq(2).css("opacity"), 1);
        }
    });
    var $content = $scrollView.find("." + SCROLLABLE_CONTENT_CLASS),
        $topPocket = $scrollView.find("." + SCROLLVIEW_TOP_POCKET_CLASS),
        $pullDownText = $scrollView.find("." + SCROLLVIEW_PULLDOWN_TEXT_CLASS);

    pointerMock($content)
        .start()
        .down()
        .move(0, $topPocket.height() + 1)
        .up();
});

QUnit.test("release cause release state", function(assert) {
    assert.expect(3);

    var clock = this.clock;

    var $scrollView = $("#scrollView").dxScrollView({
        useNative: false,
        inertiaEnabled: false,
        onPullDown: function() {
            $scrollView.dxScrollView("release");
            clock.tick();
        },
        onEnd: function() {
            assert.equal($topPocket.children().eq(0).hasClass(SCROLLVIEW_PULLDOWN_LOADING_CLASS), false, "scrollview-pull-down-loading class removed");
            assert.equal($topPocket.children().eq(0).hasClass(SCROLLVIEW_PULLDOWN_READY_CLASS), false, "scrollview-pull-down-ready class removed");
            assert.equal($pullDownText.children().eq(0).css("opacity"), 1);
        }
    });
    var $content = $scrollView.find("." + SCROLLABLE_CONTENT_CLASS),
        $topPocket = $scrollView.find("." + SCROLLVIEW_TOP_POCKET_CLASS),
        $pullDownText = $scrollView.find("." + SCROLLVIEW_PULLDOWN_TEXT_CLASS);

    pointerMock($content)
        .start()
        .down()
        .move(0, $topPocket.height() + 1)
        .up();
});

QUnit.test("scrollview locked while pulldown loading", function(assert) {
    assert.expect(0);

    var loading = false;

    var $scrollView = $("#scrollView").dxScrollView({
        useNative: false,
        inertiaEnabled: false,
        onScroll: function() {
            if(loading) {
                assert.ok(false, "scrolling disabled");
            }
        },
        onPullDown: function() {

            setTimeout(function() {
                loading = true;
                mouse
                    .down()
                    .move(0, -$topPocket.height() - 1)
                    .up();
            });
        }
    });

    var $content = $scrollView.find("." + SCROLLABLE_CONTENT_CLASS),
        $topPocket = $scrollView.find("." + SCROLLVIEW_TOP_POCKET_CLASS),
        mouse = pointerMock($content).start();

    mouse
        .down()
        .move(0, $topPocket.height() + 1)
        .up();
});

QUnit.test("scrollview onReachBottom action fired when bottom position is reached", function(assert) {
    assert.expect(1);

    var $scrollView = $("#scrollView").dxScrollView({
        useNative: false,
        inertiaEnabled: false,
        onReachBottom: function() {
            var location = getScrollOffset($scrollView);
            assert.roughEqual(location.top, $container.height() - $content.height(), 1);
        }
    });

    var $container = $scrollView.find("." + SCROLLABLE_CONTAINER_CLASS),
        $content = $scrollView.find("." + SCROLLABLE_CONTENT_CLASS),
        mouse = pointerMock($content).start();

    mouse
        .down()
        .move(0, -$content.height() - 10)
        .up();
});

QUnit.test("scrollview should go to released state if release method was called during the loading", function(assert) {
    assert.expect(1);
    var done = assert.async(),
        count = 0;

    var $scrollView = $("#scrollView").dxScrollView({
            height: 50,
            useNative: true,
            inertiaEnabled: false,
            refreshStrategy: "pullDown",
            onReachBottom: function() {
                count++;
                scrollView.release(false);
                scrollView.scrollTo({ x: 0, y: 250 });

                if(count > 1) {
                    assert.ok(true);
                    done();
                }
            }
        }),
        scrollView = $scrollView.dxScrollView("instance");

    scrollView.toggleLoading(true);
    scrollView.scrollTo({ x: 0, y: 220 });
});

QUnit.test("onReachBottom should not be called when scroll delta is 0", function(assert) {
    assert.expect(1);

    var done = assert.async(),
        $scrollView = $("#scrollView").dxScrollView({
            height: 50,
            useNative: true,
            inertiaEnabled: false,
            refreshStrategy: "pullDown",
            onReachBottom: function() {
                scrollView.release(false);

                assert.ok(true, "reachBottom was called once");
                done();
            }
        }),
        scrollView = $scrollView.dxScrollView("instance");

    scrollView.scrollTo({ x: 0, y: 150 });
    scrollView.content().trigger("scroll");
});

QUnit.test("scrollview locked while loading", function(assert) {
    assert.expect(0);

    var loading = false;

    var $scrollView = $("#scrollView").dxScrollView({
        useNative: false,
        inertiaEnabled: false,
        onScroll: function() {
            if(loading) {
                assert.ok(false, "scrolling disabled");
            }
        },
        onReachBottom: function() {
            setTimeout(function() {
                loading = true;
                mouse
                    .down()
                    .move(0, $bottomPocket.height() + 1)
                    .up();
            });
        }
    });

    var $content = $scrollView.find("." + SCROLLABLE_CONTENT_CLASS),
        $bottomPocket = $scrollView.find("." + SCROLLVIEW_BOTTOM_POCKET_CLASS),
        mouse = pointerMock($content).start();

    mouse
        .down()
        .move(0, -$content.height() - 10)
        .up();
});

QUnit.test("release after loading cause bounce to the bottom bound", function(assert) {
    assert.expect(1);

    var clock = this.clock;

    var $scrollView = $("#scrollView").dxScrollView({
        useNative: false,
        inertiaEnabled: false,
        onReachBottom: function() {
            this.release();
            clock.tick();
        },
        onEnd: function() {
            var location = getScrollOffset($scrollView);
            assert.roughEqual(location.top, $container.height() - $content.height(), 1, "scrollview bounced");
        }
    });

    var $container = $scrollView.find("." + SCROLLABLE_CONTAINER_CLASS),
        $content = $scrollView.find("." + SCROLLABLE_CONTENT_CLASS);

    pointerMock($content)
        .start()
        .down()
        .move(0, -$content.height() - 10)
        .up();
});

QUnit.test("pull down element is not hidden when container larger than content", function(assert) {
    var $scrollView = $("#scrollView").dxScrollView({ useNative: false });

    var $content = $scrollView.find("." + SCROLLABLE_CONTENT_CLASS),
        $container = $scrollView.find("." + SCROLLABLE_CONTAINER_CLASS),
        $topPocket = $scrollView.find("." + SCROLLVIEW_TOP_POCKET_CLASS);

    $container.height(400);
    $content.find("." + SCROLLVIEW_CONTENT_CLASS).children().height(50);
    $scrollView.dxScrollView("update");

    var location = getScrollOffset($scrollView);

    assert.equal(location.top, -$topPocket.height());
});

QUnit.test("pull down doesn't show loading panel", function(assert) {
    assert.expect(1);

    var isLoadPanelVisible;

    var $scrollView = $("#scrollView").dxScrollView({
        useNative: false,
        inertiaEnabled: false,
        onPullDown: function() {
            isLoadPanelVisible = $scrollView.find("." + SCROLLVIEW_LOADPANEL).eq(0).dxLoadPanel("option", "visible");
        }
    });
    var $content = $scrollView.find("." + SCROLLABLE_CONTENT_CLASS),
        $topPocket = $scrollView.find("." + SCROLLVIEW_TOP_POCKET_CLASS);

    pointerMock($content)
        .start()
        .down()
        .move(0, $topPocket.height() + 1)
        .up();

    assert.equal(isLoadPanelVisible, false, "load panel is invisible during pull down");
});

QUnit.test("scrollview content should not blink in bounce on iOS", function(assert) {
    var $scrollView = $("#scrollView").dxScrollView({
        useNative: true,
        refreshStrategy: 'pullDown'
    });
    var instance = $scrollView.dxScrollView("instance");
    var $wrapper = $scrollView.find("." + SCROLLABLE_WRAPPER_CLASS);
    var onStart = false;
    var onEnd = false;

    $wrapper
        .on("dxscrollstart", function() {
            onStart = instance._strategy._disablePushBack;
        })
        .on("dxscrollend", function() {
            onEnd = instance._strategy._disablePushBack;
        });

    pointerMock($(".content1"))
        .start()
        .down()
        .move(0, 10)
        .up();

    assert.ok(onStart, "constant _disablePushBack is true on scroll start");
    assert.ok(!onEnd, "constant _disablePushBack is false on scroll end");
});


QUnit.module("scrollbars", moduleConfig);

QUnit.test("scrollView locates scrollbar in correctly position after creation", function(assert) {
    var $scrollView = $("#scrollView").dxScrollView({
        useNative: false
    });

    var location = getScrollOffset($scrollView);
    assert.equal(location.top, 0, "scrollbar at the top position");
});

QUnit.test("scrollbar height calculated correctly", function(assert) {
    var containerHeight = 50,
        contentHeight = 100,
        scrollHeight = (containerHeight / contentHeight) * containerHeight;

    var $scrollView = $("#scrollView").dxScrollView({
        useNative: false
    });

    var $container = $scrollView.find("." + SCROLLABLE_CONTAINER_CLASS),
        $content = $scrollView.find("." + SCROLLVIEW_CONTENT_CLASS),
        $scroll = $scrollView.find("." + SCROLLABLE_SCROLL_CLASS);

    $container.height(containerHeight);
    $content.empty().height(contentHeight);

    $scrollView.dxScrollView("instance").update();

    assert.equal($scroll.height(), scrollHeight, "scrollbar height calculated correctly");
});

QUnit.test("moving scrollView moves scrollbar in correct position", function(assert) {
    var containerHeight = 50,
        contentHeight = 100,
        location,
        distance = -10,
        scrollbarDistance = -distance * (containerHeight / contentHeight);

    var $scrollView = $("#scrollView").dxScrollView({
        useNative: false,
        inertiaEnabled: false,
        onEnd: function() {
            location = translator.locate($scroll);
            assert.equal(location.top, 2 * scrollbarDistance, "scrollbar follows pointer everytime");
        }
    });

    var $content = $scrollView.find("." + SCROLLVIEW_CONTENT_CLASS),
        $container = $scrollView.find("." + SCROLLABLE_CONTAINER_CLASS),
        $scroll = $scrollView.find("." + SCROLLABLE_SCROLL_CLASS);

    $container.height(containerHeight);
    $content.empty().height(contentHeight);
    $scrollView.dxScrollView("instance").update();

    pointerMock($scrollView.find("." + SCROLLABLE_CONTENT_CLASS))
        .start()
        .down()
        .move(0, distance)
        .move(0, distance)
        .up();
});

QUnit.test("scroll over scrollbar does not hide thumb", function(assert) {
    var $scrollView = $("#scrollView").height(50);
    $scrollView.append("<div>").children().height(100);
    $scrollView.dxScrollView({
        useNative: false,
        showScrollbar: "onHover",
        bounceEnabled: false
    });

    var $scrollbar = $scrollView.find("." + SCROLLABLE_SCROLLBAR_CLASS);
    pointerMock($scrollbar).start().wheel(-10);

    assert.equal(Scrollbar.getInstance($scrollbar).option("visible"), true, "thumb stays visible when showScrollbar is 'onHover'");
});

QUnit.test("scrolling by thumb should trigger bottom loading even without moving", function(assert) {
    var containerSize = 50,
        contentHeight = 100;

    var $scrollView = $("#scrollView").dxScrollView({
        scrollByThumb: true,
        inertiaEnabled: false,
        useNative: false,
        onReachBottom: function() {
            assert.ok(true, "action fired");
        }
    });

    var $scrollbar = $scrollView.find("." + SCROLLABLE_SCROLLBAR_CLASS),
        mouse = pointerMock($scrollbar).start(),
        $container = $scrollView.find("." + SCROLLABLE_CONTAINER_CLASS),
        $content = $scrollView.find("." + SCROLLVIEW_CONTENT_CLASS);

    $container.height(containerSize).width(containerSize);
    $content.empty().height(contentHeight);
    $scrollView.dxScrollView("update");

    var containerPosition = $container.offset();
    mouse.down(containerPosition.top + containerSize, containerPosition.left + containerSize).up();
});


QUnit.module("api", moduleConfig);

QUnit.test("content", function(assert) {
    var $scrollView = $("#scrollView").dxScrollView({});
    assert.ok($scrollView.dxScrollView("instance").content().hasClass(SCROLLVIEW_CONTENT_CLASS), "returns scrollView content");
});

QUnit.test("release", function(assert) {
    assert.expect(1);

    var clock = this.clock;

    var $scrollView = $("#scrollView").dxScrollView({
        useNative: false,
        inertiaEnabled: false,
        onPullDown: function() {
            this.release();
            clock.tick();
        },
        onEnd: function() {
            var location = getScrollOffset($scrollView);
            assert.equal(location.top, -$topPocket.height(), "scrollview bounced");
        }
    });

    var $content = $scrollView.find("." + SCROLLABLE_CONTENT_CLASS),
        $topPocket = $scrollView.find("." + SCROLLVIEW_TOP_POCKET_CLASS);

    pointerMock($content)
        .start()
        .down()
        .move(0, $topPocket.height() + 1)
        .up();
});

QUnit.test("release does not enable reach bottom functionality", function(assert) {
    assert.expect(2);

    var $scrollView = $("#scrollView").dxScrollView({
        useNative: false,
        inertiaEnabled: false
    });

    var $reachBottom = $scrollView.find("." + SCROLLVIEW_REACHBOTTOM_CLASS);

    assert.ok($reachBottom.is(":hidden"), "reach bottom is hidden");

    $scrollView.dxScrollView("release").done(function() {
        assert.ok($reachBottom.is(":hidden"), "reach bottom is hidden");
    });

    this.clock.tick();
});

QUnit.test("release with preventReachBottom", function(assert) {
    assert.expect(1);

    var clock = this.clock;

    var $scrollView = $("#scrollView").dxScrollView({
        useNative: false,
        inertiaEnabled: false,
        onPullDown: function() {
            this.release(true);
            clock.tick();
        },
        onEnd: function() {
            var $bottomPocketLoading = $scrollView.find("." + SCROLLVIEW_REACHBOTTOM_CLASS);
            assert.ok($bottomPocketLoading.is(":hidden"), "bottom pocket loading is hidden");
        }
    });

    var $content = $scrollView.find("." + SCROLLABLE_CONTENT_CLASS),
        $topPocket = $scrollView.find("." + SCROLLVIEW_TOP_POCKET_CLASS);

    pointerMock($content)
        .start()
        .down()
        .move(0, $topPocket.height() + 1)
        .up();
});

QUnit.test("release without pulldown", function(assert) {
    assert.expect(1);

    var $scrollView = $("#scrollView").dxScrollView({
        useNative: false
    });

    $scrollView.dxScrollView("release")
        .done(function() {
            assert.ok(true, "release without loading fails");
        });

    this.clock.tick();
});

QUnit.test("release fires update", function(assert) {
    var updated = 0;

    var $scrollView = $("#scrollView").dxScrollView({
        useNative: false,
        onUpdated: function() {
            updated++;
        }
    });
    assert.equal(updated, 1, "update fired once after creation");

    $scrollView.dxScrollView("release");

    this.clock.tick();

    assert.equal(updated, 2, "update fired");
});

QUnit.test("release calls update", function(assert) {
    assert.expect(1);

    var $scrollView = $("#scrollView").dxScrollView({
        useNative: false,
        inertiaEnabled: false,
        onPullDown: function() {
            $(".content2").height(400);
            setTimeout($.proxy(this.release, this));
        },
        onEnd: function() {
            mouse
                .start()
                .down()
                .move(0, -1000)
                .up();
        },
        onReachBottom: function() {
            var location = getScrollOffset($scrollView);
            assert.roughEqual(location.top, $container.height() - $content.height(), 1);
        }
    });

    var $container = $scrollView.find(".dx-scrollable-container"),
        $content = $scrollView.find("." + SCROLLABLE_CONTENT_CLASS),
        $topPocket = $scrollView.find("." + SCROLLVIEW_TOP_POCKET_CLASS);

    var mouse = pointerMock($content).start();

    mouse
        .down()
        .move(0, $topPocket.height() + 1)
        .up();

    this.clock.tick();
});

QUnit.test("release calls update for scrollbar", function(assert) {
    assert.expect(1);

    var $scrollView = $("#scrollView").dxScrollView({
        useNative: false,
        onPullDown: $.noop,
        inertiaEnabled: false,
        onEnd: function() {
            assert.equal($scroll.height(), Math.pow($container.height(), 2) / $content.height());
        },
        onReachBottom: function() {
            $container.height(100);
            $(".content2").height(400);
            setTimeout($.proxy(this.release, this));
        }
    });

    var $container = $scrollView.find(".dx-scrollable-container"),
        $content = $scrollView.find("." + SCROLLVIEW_CONTENT_CLASS),
        $topPocket = $scrollView.find("." + SCROLLVIEW_TOP_POCKET_CLASS),
        $scroll = $scrollView.find("." + SCROLLABLE_SCROLL_CLASS);

    pointerMock($content)
        .start()
        .down()
        .move(0, $container.height() - $content.height() - $topPocket.height() - 10)
        .up();

    this.clock.tick();
});

QUnit.test("release calls moveToBound location immediately when state is released", function(assert) {
    var $scrollView = $("#scrollView").dxScrollView({
        useNative: false,
        onPullDown: $.noop,
        inertiaEnabled: true
    });

    var $children = $("." + SCROLLVIEW_CONTENT_CLASS, $scrollView).children();
    var $scrollableContent = $("." + SCROLLABLE_CONTENT_CLASS, $scrollView);
    var pullDownSize = $("." + SCROLLVIEW_TOP_POCKET_CLASS, $scrollView).height();
    var scrollView = $scrollView.dxScrollView("instance");

    scrollView.scrollTo(scrollView.scrollHeight());
    $children.remove();
    pointerMock($scrollableContent).start().down(); // NOTE: call update without moveToBound location
    scrollView.release();

    var locate = getScrollOffset($scrollView);
    assert.equal(locate.top, -pullDownSize, "moveToBound was called immediately after release");
});

QUnit.test("toggleLoading", function(assert) {
    var $scrollView = $("#scrollView").dxScrollView({
            useNative: false,
            onReachBottom: $.noop
        }),
        $bottomPocketLoading = $scrollView.find("." + SCROLLVIEW_REACHBOTTOM_CLASS),
        scrollView = $scrollView.dxScrollView("instance");

    assert.ok($bottomPocketLoading.is(":visible"), "loading is visible at the beginning");

    scrollView.toggleLoading(false);
    assert.ok($bottomPocketLoading.is(":hidden"), "loading is hidden");

    scrollView.toggleLoading(true);
    assert.ok($bottomPocketLoading.is(":visible"), "loading is visible");
});

QUnit.test("toggleLoading turns off reachBottom behavior", function(assert) {
    var $scrollView = $("#scrollView").dxScrollView({
        useNative: false,
        inertiaEnabled: false,
        onReachBottom: function(e) {
            assert.ok(false, "onReachBottom action should not be fired");
        },
        onEnd: function() {
            assert.ok(true, "end action should be fired");
            var location = getScrollOffset($scrollView);
            assert.equal(location.top, $container.height() - $content.height() + $bottomPocket.height());
        }
    });

    var $container = $scrollView.find(".dx-scrollable-container"),
        $content = $scrollView.find("." + SCROLLABLE_CONTENT_CLASS),
        $bottomPocket = $scrollView.find("." + SCROLLVIEW_BOTTOM_POCKET_CLASS);

    var mouse = pointerMock($content)
        .start()
        .down()
        .move(0, $container.height() - $content.height() - $bottomPocket.height() - 1);

    $scrollView.dxScrollView("toggleLoading", false);

    mouse.up();
});

QUnit.test("isFull", function(assert) {
    var $scrollView = $("#scrollView").dxScrollView({}),
        $container = $scrollView.find(".dx-scrollable-container"),
        $content = $scrollView.find("." + SCROLLVIEW_CONTENT_CLASS);

    $container.height(50);
    $content.children().height(100);
    $scrollView.dxScrollView("update");
    assert.equal($scrollView.dxScrollView("isFull"), true, "container is full");
});

QUnit.test("isFull should be false if container has same height as content", function(assert) {
    var $scrollView = $("#scrollView").dxScrollView({}),
        $container = $scrollView.find(".dx-scrollable-container"),
        $content = $scrollView.find("." + SCROLLVIEW_CONTENT_CLASS);

    $container.height(50);
    $content.children().height(25);
    $scrollView.dxScrollView("update");
    assert.equal($scrollView.dxScrollView("isFull"), false, "container is full");
});

QUnit.test("refresh", function(assert) {
    assert.expect(2);

    var pullDownFired = 0;
    var deferred = $.Deferred();

    var scrollView = $("#scrollView").dxScrollView({
        onPullDown: function(e) {
            pullDownFired++;

            e.component.release().done(function() {
                deferred.resolve();
            });
        }
    }).dxScrollView("instance");

    scrollView.refresh();

    deferred.done(function() {
        assert.ok(true, "scroll view released");
    });

    assert.equal(pullDownFired, 1, "pull down action fired once");

    this.clock.tick(1000); // NOTE: wait complete for all strategies
});

QUnit.test("refresh show load panel", function(assert) {
    var deferred = $.Deferred();

    var $scrollView = $("#scrollView");

    var scrollView = $scrollView.dxScrollView({
        onPullDown: function(e) {
            assert.equal(loadPanel.option("visible"), true, "load panel shown on start");

            e.component.release().done(function() {
                deferred.resolve();
            });
        }
    }).dxScrollView("instance");

    var loadPanel = $scrollView.find("." + SCROLLVIEW_LOADPANEL).dxLoadPanel("instance");

    scrollView.refresh();

    deferred.done(function() {
        assert.equal(loadPanel.option("visible"), false, "load panel hidden on done");
    });
});

QUnit.test("refreshingText pass to dxLoadPanel", function(assert) {
    var testRefreshingText = "test";
    var $scrollView = $("#scrollView").dxScrollView({
        refreshingText: testRefreshingText
    });

    var loadPanel = $scrollView.find("." + SCROLLVIEW_LOADPANEL).dxLoadPanel("instance");

    assert.equal(loadPanel.option("message"), testRefreshingText, "refreshingText pass to loadPanel");
});

QUnit.test("startLoading shows load panel and locks scrolling", function(assert) {
    var $scrollView = $("#scrollView");
    var wasScrollEvent;
    $scrollView.dxScrollView({
        onScroll: function() {
            wasScrollEvent = true;
        }
    });
    wasScrollEvent = false;
    var loadPanel = $scrollView.find("." + SCROLLVIEW_LOADPANEL).dxLoadPanel("instance");

    $scrollView.dxScrollView("startLoading");
    assert.equal(loadPanel.option("visible"), true, "load panel shown");

    pointerMock($scrollView).start().down().move(0, 10);
    assert.ok(!wasScrollEvent, "scrollEvent was not fired");
});

QUnit.test("startLoading shows load panel doesn't show load panel when control is hidden", function(assert) {
    var $scrollView = $("#scrollView");
    $scrollView.dxScrollView({});

    var loadPanel = $scrollView.find("." + SCROLLVIEW_LOADPANEL).dxLoadPanel("instance");
    $scrollView.hide();

    $scrollView.dxScrollView("startLoading");
    assert.equal(loadPanel.option("visible"), false, "load panel shown");
});

QUnit.test("finishLoading hides load panel and unlocks scrolling", function(assert) {
    var $scrollView = $("#scrollView");
    $scrollView.dxScrollView({
        onScroll: function() {
            assert.ok(true, "scroll is disabled");
        }
    });

    var loadPanel = $scrollView.find("." + SCROLLVIEW_LOADPANEL).dxLoadPanel("instance");

    $scrollView.dxScrollView("startLoading");
    $scrollView.dxScrollView("finishLoading");
    assert.equal(loadPanel.option("visible"), false, "load panel hidden");

    pointerMock($scrollView).start().down().move(0, 10);
});

QUnit.test("load panel is not shown when onPullDown is not specified", function(assert) {
    var $scrollView = $("#scrollView");
    var scrollView = $scrollView.dxScrollView().dxScrollView("instance");
    var loadPanel = $scrollView.find("." + SCROLLVIEW_LOADPANEL).dxLoadPanel("instance");

    scrollView.refresh();
    assert.equal(loadPanel.option("visible"), false, "load panel hidden");
});

QUnit.test("disabled: scroll was not moved when disabled is changed dynamically", function(assert) {
    var $scrollView = $("#scrollView").dxScrollView({
            useNative: false
        }),
        scrollView = $scrollView.dxScrollView("instance");

    scrollView.scrollTo({ top: 20 });
    scrollView.option("disabled", true);

    assert.equal(scrollView.scrollTop(), 20, "scroll was not moved when disabled is changed dynamically");
});

QUnit.test("reach bottom action fired when scrolled to bottom", function(assert) {
    assert.expect(1);

    var $scrollView = $("#scrollView").dxScrollView({
        bounceEnabled: false,
        onReachBottom: function(e) {
            assert.ok(true, "reachBottom fired");
        },
        useNative: false
    });
    var scrollView = $scrollView.dxScrollView("instance");
    var bottomPocketHeight = $scrollView.find("." + SCROLLVIEW_REACHBOTTOM_CLASS).height();
    scrollView.scrollTo(scrollView.content().height() + bottomPocketHeight);
});

QUnit.test("loadPanel hiding after release", function(assert) {
    var $scrollView = $("#scrollView").dxScrollView({
        onPullDown: $.noop,
        useNative: false,
        inertiaEnabled: false,
    });
    var scrollView = $scrollView.dxScrollView("instance");
    var $topPocket = $scrollView.find("." + SCROLLVIEW_TOP_POCKET_CLASS);

    scrollView.startLoading();
    scrollView.scrollTo(-$topPocket.height() - 1);
    scrollView.finishLoading();

    var loadPanel = $scrollView.find(".dx-loadpanel").dxLoadPanel("instance");
    assert.equal(loadPanel.option("visible"), false, "load panel hiding");
});


QUnit.test("scrollTo considers pullDown", function(assert) {
    var $scrollView = $("#scrollView").dxScrollView({
        useNative: false,
        onPullDown: $.noop,
        bounceEnabled: true
    });
    var scrollView = $scrollView.dxScrollView("instance");
    var initialScrollTop = scrollView.scrollTop();
    assert.equal(initialScrollTop, 0, "scrollTop has 0 position on start");

    scrollView.scrollTo(0);

    assert.equal(scrollView.scrollTop(), initialScrollTop, "scrollTop not changed");
});

QUnit.module("native pullDown strategy", {
    beforeEach: function() {
        moduleConfig.beforeEach.call(this);
        this._originalPlatform = devices.real().platform;
        devices.real({ platform: "ios" });
        $("#qunit-fixture").addClass("dx-theme-ios");

        this.originalJQueryScrollTop = $.fn.scrollTop;
        var currentValue = 0;
        $.fn.scrollTop = function(value) {
            if(arguments.length) {
                currentValue = value;
            } else {
                return currentValue;
            }
        };
    },
    afterEach: function() {
        moduleConfig.afterEach.call(this);
        devices.real({ platform: this._originalPlatform });
        $("#qunit-fixture").removeClass("dx-theme-ios");

        $.fn.scrollTop = this.originalJQueryScrollTop;
    }
});

var transformNotSupported = !support.transform || browser.msie && browser.version <= 9;

QUnit.test("markup", function(assert) {
    var $scrollView = $("#scrollView").dxScrollView({
        useNative: true,
        refreshStrategy: "pullDown"
    });

    var $pullDown = $scrollView.find("." + SCROLLVIEW_PULLDOWN_CLASS);
    assert.equal($pullDown.length, 1, "pull down container");
    assert.equal($pullDown.find("." + SCROLLVIEW_PULLDOWN_IMAGE_CLASS).length, 1, "pull down image");
    assert.equal($pullDown.find("." + SCROLLVIEW_PULLDOWN_INDICATOR_CLASS).length, 1, "pull down load indicator container");
    assert.equal($pullDown.find("." + SCROLLVIEW_PULLDOWN_TEXT_CLASS).length, 1, "pull down text");
});

QUnit.test("pull down element position", function(assert) {
    if(transformNotSupported) {
        assert.expect(0);
        return;
    }

    var $scrollView = $("#scrollView").dxScrollView({
        useNative: true,
        refreshStrategy: "pullDown",
        onPullDown: $.noop
    });

    var $container = $("." + SCROLLABLE_CONTAINER_CLASS, $scrollView),
        $topPocket = $("." + SCROLLVIEW_TOP_POCKET_CLASS, $scrollView);

    var containerOffset = $container.offset().top,
        topPocketOffset = $topPocket.offset().top,
        topPocketSize = $topPocket.height();

    assert.equal(containerOffset, topPocketOffset + topPocketSize, "pull down element located above content");
});

QUnit.test("pull down element position after dynamic action specification", function(assert) {
    if(transformNotSupported) {
        assert.expect(0);
        return;
    }

    var $scrollView = $("#scrollView").dxScrollView({
        useNative: true,
        refreshStrategy: "pullDown"
    });
    $scrollView.dxScrollView("option", "onPullDown", $.noop);

    var $container = $("." + SCROLLABLE_CONTAINER_CLASS, $scrollView),
        $topPocket = $("." + SCROLLVIEW_TOP_POCKET_CLASS, $scrollView);

    var containerOffset = $container.offset().top,
        topPocketOffset = $topPocket.offset().top,
        topPocketSize = $topPocket.height();

    assert.equal(containerOffset, topPocketOffset + topPocketSize, "pull down element located above content");
});

QUnit.test("scrollTop should be greater than 0 on init for prevent WebView bounce", function(assert) {
    var $scrollView = $("#scrollView").dxScrollView({
        useNative: true,
        refreshStrategy: "pullDown"
    });

    var $container = $("." + SCROLLABLE_CONTAINER_CLASS, $scrollView);

    assert.equal($container.scrollTop(), 1, "real scrollTop is greater than 0");
});

QUnit.test("scrollTop should be greater than 0 after scroll event for prevent WebView bounce", function(assert) {
    var $scrollView = $("#scrollView").dxScrollView({
        useNative: true,
        refreshStrategy: "pullDown"
    });

    var scrollView = $("#scrollView").dxScrollView("instance"),
        $container = $("." + SCROLLABLE_CONTAINER_CLASS, $scrollView);

    scrollView.scrollTo({ y: 10 });
    $container.trigger("scroll");
    assert.equal($container.scrollTop(), 11, "container was scrolled");

    scrollView.scrollTo({ y: 0 });
    $container.trigger("scroll");
    assert.equal($container.scrollTop(), 1, "scrollTop is greater than 0");
});

QUnit.test("scrollTop method should have correct position on init", function(assert) {
    var scrollView = $("#scrollView").dxScrollView({
        useNative: true,
        refreshStrategy: "pullDown"
    }).dxScrollView("instance");

    assert.equal(scrollView.scrollTop(), 0, "scrollTop is 0");
});

QUnit.test("scrollTop method should have correct position after scroll event", function(assert) {
    var $scrollView = $("#scrollView").dxScrollView({
        useNative: true,
        refreshStrategy: "pullDown"
    });

    var scrollView = $("#scrollView").dxScrollView("instance"),
        $container = $("." + SCROLLABLE_CONTAINER_CLASS, $scrollView);

    scrollView.scrollTo({ y: 10 });
    $container.trigger("scroll");
    assert.equal(scrollView.scrollTop(), 10, "correct scroll position");

    scrollView.scrollTo({ y: 0 });
    $container.trigger("scroll");
    assert.equal(scrollView.scrollTop(), 0, "correct scroll position");
});

QUnit.test("pulled down adds ready state", function(assert) {
    if(transformNotSupported) {
        assert.expect(0);
        return;
    }

    var $scrollView = $("#scrollView").dxScrollView({
        useNative: true,
        refreshStrategy: "pullDown",
        onPullDown: $.noop
    });

    var scrollView = $("#scrollView").dxScrollView("instance"),
        $container = $scrollView.find("." + SCROLLABLE_CONTAINER_CLASS),
        $topPocket = $scrollView.find("." + SCROLLVIEW_TOP_POCKET_CLASS),
        $pullDownText = $scrollView.find("." + SCROLLVIEW_PULLDOWN_TEXT_CLASS),
        topPocketHeight = $topPocket.height();

    scrollView.scrollTo({ y: -topPocketHeight });
    $container.trigger("scroll");

    assert.equal($topPocket.children().eq(0).hasClass(SCROLLVIEW_PULLDOWN_READY_CLASS), true, "scrollview-pull-down-ready class added");
    assert.equal($pullDownText.children().eq(1).css("opacity"), 1, "pullDown ready text");

    scrollView.scrollTo({ y: -topPocketHeight + 1 });
    $container.trigger("scroll");
    assert.equal($topPocket.children().eq(0).hasClass(SCROLLVIEW_PULLDOWN_READY_CLASS), false, "scrollview-pull-down-ready class removed");
    assert.equal($pullDownText.children().eq(0).css("opacity"), 1, "pullDown to refresh text");
});

QUnit.test("onPullDown action", function(assert) {
    if(transformNotSupported) {
        assert.expect(0);
        return;
    }

    var $scrollView = $("#scrollView").dxScrollView({
        useNative: true,
        refreshStrategy: "pullDown",
        onPullDown: function() {
            assert.ok(true, "onPullDown action was fired");
            var location = getScrollOffset($scrollView).top;
            assert.equal(location, topPocketHeight, "topPocket located at the top of container");
            location = getScrollOffset($scrollView).top;
            assert.equal(location, topPocketHeight, "content located below the top pocket");

            assert.equal($topPocket.children().eq(0).hasClass(SCROLLVIEW_PULLDOWN_LOADING_CLASS), true, "scrollview-pull-down-refreshing class added");
            assert.equal($pullDownText.children().eq(2).css("opacity"), 1, "pullDown refreshing text");
        }
    });

    $("*", $scrollView).css({
        "transition": "none"
    });

    var $container = $scrollView.find("." + SCROLLABLE_CONTAINER_CLASS),
        $topPocket = $scrollView.find("." + SCROLLVIEW_TOP_POCKET_CLASS),
        $pullDownText = $scrollView.find("." + SCROLLVIEW_PULLDOWN_TEXT_CLASS),
        topPocketHeight = $topPocket.height();


    var pointer = pointerMock($container).start().down();
    $container.scrollTop(-topPocketHeight);
    $container.trigger("scroll");
    pointer.up();

    this.clock.tick(400);
});

QUnit.test("release", function(assert) {
    if(transformNotSupported) {
        assert.expect(0);
        return;
    }

    devices.real({ platform: this._originalPlatform });

    var $scrollView = $("#scrollView").dxScrollView({
            useNative: true,
            refreshStrategy: "pullDown",
            onPullDown: function() {
                setTimeout(function() {
                    scrollView.release();
                });
            }
        }),
        scrollView = $scrollView.dxScrollView("instance");

    var $wrapper = $scrollView.find("." + SCROLLABLE_WRAPPER_CLASS),
        $container = $scrollView.find("." + SCROLLABLE_CONTAINER_CLASS),
        $topPocket = $scrollView.find("." + SCROLLVIEW_TOP_POCKET_CLASS),
        $pullDownText = $scrollView.find("." + SCROLLVIEW_PULLDOWN_TEXT_CLASS),
        topPocketHeight = $topPocket.height();

    var pointer = pointerMock($container)
            .start()
            .down();
    $container.scrollTop(-topPocketHeight);
    $wrapper.trigger("scroll");
    pointer.up();

    this.clock.tick(800);
    assert.ok(true, "scrollView was enabled");
    var location = translator.locate($topPocket).top;
    assert.equal(location, 0, "topPocket located above content");
    location = translator.locate($scrollView).top;
    assert.equal(location, 0, "content located at the top of container");

    assert.equal($topPocket.children().eq(0).hasClass(SCROLLVIEW_PULLDOWN_LOADING_CLASS), false, "scrollview-pull-down-refreshing class added");
    assert.equal($pullDownText.children().eq(2).css("opacity"), 0, "pullDown refreshing text");
});

QUnit.test("onReachBottom", function(assert) {
    if(transformNotSupported) {
        assert.expect(0);
        return;
    }

    assert.expect(1);

    var $scrollView = $("#scrollView").dxScrollView({
        useNative: true,
        refreshStrategy: "pullDown",
        onReachBottom: function() {
            assert.ok(true, "onReachBottom action was fired");
        }
    });

    var $container = $scrollView.find("." + SCROLLABLE_CONTAINER_CLASS),
        $content = $scrollView.find("." + SCROLLABLE_CONTENT_CLASS),
        $bottomPocket = $scrollView.find("." + SCROLLVIEW_BOTTOM_POCKET_CLASS);

    $container.scrollTop($content.height() - $container.height() + $bottomPocket.height() + 1);
    $container.trigger("scroll");
});

QUnit.test("release fires update", function(assert) {
    if(transformNotSupported) {
        assert.expect(0);
        return;
    }

    var updated = 0;

    var $scrollView = $("#scrollView").dxScrollView({
        useNative: true,
        onUpdated: function() {
            updated++;
        }
    });
    assert.equal(updated, 1, "update fired once after creation");
    var clock = sinon.useFakeTimers();
    try {
        $scrollView.dxScrollView("release");
        clock.tick(400);
        assert.equal(updated, 2, "update fired");

    } finally {
        clock.restore();
    }
});

QUnit.test("scroll fires with correctly arguments", function(assert) {
    if(transformNotSupported || this._originalPlatform === "android" && devices.real().version[0] < 4) {
        assert.expect(0);
        return;
    }

    var top = true,
        left = true,
        right = false,
        bottom = false;
    var lastScrollEventArgs;
    var $scrollView = $("#scrollView").width(50).height(50);
    $scrollView.children().width(100).height(100);

    var checkLastScrollEvent = function() {
        assert.equal(lastScrollEventArgs.reachedTop, top, "reached top is correct");
        assert.equal(lastScrollEventArgs.reachedRight, right, "reached right is correct");
        assert.equal(lastScrollEventArgs.reachedBottom, bottom, "reached bottom is correct");
        assert.equal(lastScrollEventArgs.reachedLeft, left, "reachde left is correct");
    };

    $scrollView.dxScrollView({
        useNative: true,
        direction: "both",
        onScroll: function(e) {
            lastScrollEventArgs = e;
        }
    });

    var scrollView = $scrollView.dxScrollView("instance");
    var $container = $("." + SCROLLABLE_CONTAINER_CLASS, $scrollView);

    assert.ok(!lastScrollEventArgs, "scroll was not triggered on start");

    $container.trigger("scroll");
    checkLastScrollEvent();

    scrollView.scrollTo({ x: 1, y: 1 });
    top = false; left = false;
    $container.trigger("scroll");
    checkLastScrollEvent();

    scrollView.scrollTo({
        x: $container.prop("scrollWidth") - $container.prop("clientWidth"),
        y: $container.prop("scrollHeight") - $container.prop("clientHeight")
    });
    right = true; bottom = true;
    $container.trigger("scroll");
    checkLastScrollEvent();
});

QUnit.module("native swipeDown strategy", {
    beforeEach: function() {
        moduleConfig.beforeEach.call(this);
        this._originalPlatform = devices.real().platform;
        devices.real({ platform: "android" });
        $("#qunit-fixture").addClass("dx-theme-android");
    },
    afterEach: function() {
        moduleConfig.afterEach.call(this);
        devices.real({ platform: this._originalPlatform });
        $("#qunit-fixture").removeClass("dx-theme-android");
    }
});

QUnit.test("markup", function(assert) {
    var $scrollView = $("#scrollView").dxScrollView({
        useNative: true,
        refreshStrategy: "swipeDown"
    });

    var $pullDown = $scrollView.find("." + SCROLLVIEW_PULLDOWN_CLASS);
    assert.equal($pullDown.length, 1, "pull down container");
});

QUnit.test("topPocket visibility depends on onPullDown", function(assert) {
    var $scrollView = $("#scrollView").dxScrollView({
        useNative: true
    });

    var $topPocket = $scrollView.find("." + SCROLLVIEW_TOP_POCKET_CLASS);
    assert.ok($topPocket.is(":hidden"), "topPocket hidden");

    $scrollView.dxScrollView("option", "onPullDown", $.noop);
    assert.ok($topPocket.is(":visible"), "topPocket visible");
});

QUnit.test("pull down element start state", function(assert) {
    var $scrollView = $("#scrollView").dxScrollView({
        useNative: true,
        refreshStrategy: "swipeDown"
    });

    var $pullDown = $scrollView.find("." + SCROLLVIEW_PULLDOWN_CLASS);
    assert.equal($pullDown.css("opacity"), 0, "pullDown hide with opacity");
});

QUnit.test("pulled down action should be fired when content less than container (T105659)", function(assert) {
    var $scrollView = $("#scrollView").dxScrollView({
        useNative: true,
        refreshStrategy: "swipeDown",
        onPullDown: function() {
            assert.ok(true, "pullDown fired");
        }
    });

    $scrollView.dxScrollView("content").height(10);
    $scrollView.dxScrollView("update");

    var $container = $scrollView.find("." + SCROLLABLE_CONTAINER_CLASS);

    pointerMock($container)
        .start()
        .down()
        .move(0, PULLDOWN_HEIGHT / 2)
        .move(0, PULLDOWN_HEIGHT / 2)
        .up();

    this.clock.tick(400);
});

QUnit.test("pulled down action should be fired after pointer is up if the 'swipeDown' strategy is used", function(assert) {
    var isPullDownActionFired = false,
        $scrollView = $("#scrollView").dxScrollView({
            useNative: true,
            refreshStrategy: "swipeDown",
            onPullDown: function() {
                isPullDownActionFired = true;
            }
        });

    $scrollView.dxScrollView("content").height(10);
    $scrollView.dxScrollView("update");

    var $container = $scrollView.find("." + SCROLLABLE_CONTAINER_CLASS);

    var pointer = pointerMock($container)
        .start()
        .down()
        .move(0, PULLDOWN_HEIGHT / 2)
        .move(0, PULLDOWN_HEIGHT / 2);

    assert.ok(!isPullDownActionFired, "pull down action is not fired");

    pointer.up();
    assert.ok(isPullDownActionFired, "pull down action is fired after pointer is up");
});

QUnit.test("pulled down action should not be fired at the end of scrollview", function(assert) {
    var isPullDownActionFired = false,
        $scrollView = $("#scrollView").dxScrollView({
            useNative: true,
            refreshStrategy: "swipeDown",
            onPullDown: function() {
                isPullDownActionFired = true;
            },
            height: 100
        });

    $scrollView.dxScrollView("content").height(1000);
    $scrollView.dxScrollView("update");

    var $container = $scrollView.find("." + SCROLLABLE_CONTAINER_CLASS);

    pointerMock($container).start().down().move(0, -500).up();
    $scrollView.dxScrollView("scrollTo", { y: 500 });
    $container.trigger("scroll");
    this.clock.tick(400);

    pointerMock($container).start().down(0, 0).move(0, PULLDOWN_HEIGHT).up();
    assert.ok(!isPullDownActionFired, "pull down action is not fired");
});

QUnit.test("release", function(assert) {
    var $scrollView = $("#scrollView").dxScrollView({
        useNative: true,
        refreshStrategy: "swipeDown",
        onPullDown: function() {
            scrollView.release();
        }
    });

    var scrollView = $scrollView.dxScrollView("instance");

    var $pullDown = $scrollView.find("." + SCROLLVIEW_PULLDOWN_CLASS),
        $container = $scrollView.find("." + SCROLLABLE_CONTAINER_CLASS);

    pointerMock($container)
        .start()
        .down()
        .move(0, PULLDOWN_HEIGHT);

    assert.ok(true, "scrollView was enabled");
    assert.equal($pullDown.hasClass(SCROLLVIEW_PULLDOWN_LOADING_CLASS), false, "scrollview-pull-down-loading class removed");
});

QUnit.test("onReachBottom", function(assert) {
    assert.expect(1);

    var $scrollView = $("#scrollView").dxScrollView({
        useNative: true,
        refreshStrategy: "swipeDown",
        onReachBottom: function() {
            assert.ok(true, "onReachBottom action was fired");
        }
    });

    var $container = $scrollView.find("." + SCROLLABLE_CONTAINER_CLASS),
        $content = $scrollView.find("." + SCROLLABLE_CONTENT_CLASS),
        $bottomPocket = $scrollView.find("." + SCROLLVIEW_BOTTOM_POCKET_CLASS);

    $container.scrollTop($content.height() - $container.height() + $bottomPocket.height() + 1);
    $container.trigger("scroll");
});

QUnit.test("onReachBottom action when content size changed", function(assert) {
    var count = 0;
    var $scrollView = $("#scrollView").dxScrollView({
        useNative: true,
        refreshStrategy: "swipeDown",
        onReachBottom: function() {
            count++;
        }
    });

    var $container = $scrollView.find("." + SCROLLABLE_CONTAINER_CLASS),
        $content = $scrollView.find("." + SCROLLABLE_CONTENT_CLASS),
        $bottomPocket = $scrollView.find("." + SCROLLVIEW_BOTTOM_POCKET_CLASS);

    $container.scrollTop($content.height() - $container.height() + $bottomPocket.height() + 1);
    $container.trigger("scroll");
    assert.equal(count, 1, "fire onReachBottom after first scroll");

    $container.trigger("scroll");
    assert.equal(count, 1, "onReachBottom doesn't fire if scroll position doesn't change");

    $content.height($content.height() * 2);
    $scrollView.dxScrollView("instance").release();

    $container.scrollTop($content.height() - $container.height() + $bottomPocket.height() + 1);
    $container.trigger("scroll");
    assert.equal(count, 2, "fire onReachBottom after second scroll");
});

QUnit.test("allow pullDown in Android Chrome for element with native scrolling", function(assert) {
    assert.expect(1);

    var $scrollView = $("#scrollView").dxScrollView({
        useNative: true,
        onPullDown: $.noop,
        refreshStrategy: "swipeDown"
    });

    var $container = $scrollView.find("." + SCROLLABLE_CONTAINER_CLASS);

    $(document).on("dxpointermove.dxtestns", function(e) {
        if(e.isDefaultPrevented()) {
            assert.ok(true, "move prevented");
        }
    });

    var pointer = pointerMock($container)
        .start()
        .down()
        .move(0, 10)
        .up();

    pointer
        .start()
        .move(0, -10)
        .up();

    $(document).off(".dxtestns");
});

QUnit.test("dxpointermove shouldn't be prevented if pullDown is displayed in Android Chrome", function(assert) {
    assert.expect(1);

    var $scrollView = $("#scrollView").dxScrollView({
        useNative: true,
        refreshStrategy: "swipeDown"
    });

    var $container = $scrollView.find("." + SCROLLABLE_CONTAINER_CLASS);

    $(document).on("dxpointermove.dxtestns", function(e) {
        assert.ok(!e.isDefaultPrevented(), "move is not prevented");
    });

    pointerMock($container)
        .start()
        .down()
        .move(0, 10)
        .up();

    $(document).off(".dxtestns");
});

QUnit.test("release fires update", function(assert) {
    if(!("ontouchstart" in window)) {
        assert.ok(true, "Ziborov: temporary we do not test this case if browser does not supported touch");
        return;
    }
    var updated = 0;

    var $scrollView = $("#scrollView").dxScrollView({
        useNative: true,
        onUpdated: function() {
            updated++;
        }
    });
    assert.equal(updated, 1, "update fired once after creation");
    var clock = sinon.useFakeTimers();
    try {
        $scrollView.dxScrollView("release");
        clock.tick(800);
        assert.equal(updated, 2, "update fired");

    } finally {
        clock.restore();
    }
});

QUnit.test("scroll fires with correctly arguments", function(assert) {
    if(transformNotSupported || this._originalPlatform === "android" && devices.real().version[0] < 4) {
        assert.expect(0);
        return;
    }

    assert.expect(12);

    var top = true,
        left = true,
        right = false,
        bottom = false;

    var $scrollView = $("#scrollView").width(50).height(50);
    $scrollView.children().width(100).height(100);

    $scrollView.dxScrollView({
        useNative: true,
        direction: "both",
        onScroll: function(e) {
            assert.equal(e.reachedTop, top, "reached top is correct");
            assert.equal(e.reachedLeft, left, "reached left is correct");
            assert.equal(e.reachedRight, right, "reached right is correct");
            assert.equal(e.reachedBottom, bottom, "reached bottom is correct");
        }
    });
    var scrollView = $scrollView.dxScrollView("instance");
    var $container = $("." + SCROLLABLE_CONTAINER_CLASS, $scrollView);

    $container.trigger("scroll");

    scrollView.scrollTo({ x: 1, y: 1 });
    top = false; left = false;
    $container.trigger("scroll");
    scrollView.scrollTo({
        x: $container.prop("scrollWidth") - $container.prop("clientWidth"),
        y: $container.prop("scrollHeight") - $container.prop("clientHeight")
    });
    right = true; bottom = true;
    $container.trigger("scroll");
});


QUnit.module("native slideDown strategy", {
    beforeEach: function() {
        this.originalJQueryScrollTop = $.fn.scrollTop;

        var currentTopValue = 0;
        $.fn.scrollTop = function(value) {
            if(arguments.length) {
                currentTopValue = value;
            } else {
                return currentTopValue;
            }
        };
        var currentLeftValue = 0;
        $.fn.scrollLeft = function(value) {
            if(arguments.length) {
                currentLeftValue = value;
            } else {
                return currentLeftValue;
            }
        };

        moduleConfig.beforeEach.call(this);
        this._originalPlatform = devices.real().platform;
        devices.real({ platform: "win" });
        $("#qunit-fixture").addClass("dx-theme-win8");
    },
    afterEach: function() {
        $.fn.scrollTop = this.originalJQueryScrollTop;
        moduleConfig.afterEach.call(this);
        devices.real({ platform: this._originalPlatform });
        $("#qunit-fixture").removeClass("dx-theme-win8");
    }
});

QUnit.test("markup", function(assert) {
    if(transformNotSupported) {
        assert.expect(0);
        return;
    }

    var $scrollView = $("#scrollView").dxScrollView({
        useNative: true,
        refreshStrategy: "slideDown",
        onPullDown: $.noop
    });

    var $pullDown = $scrollView.find("." + SCROLLVIEW_PULLDOWN_CLASS),
        $reachBottom = $scrollView.find("." + SCROLLVIEW_BOTTOM_POCKET_CLASS),
        $content = $scrollView.find("." + SCROLLABLE_CONTENT_CLASS),
        $topPocket = $scrollView.find("." + SCROLLVIEW_TOP_POCKET_CLASS);

    assert.equal($pullDown.length, 0, "pull down container");
    assert.equal($reachBottom.length, 1, "reach bottom container");
    assert.equal($content.scrollTop(), $topPocket.height(), "content start on correctly position");
});

QUnit.test("onReachBottom", function(assert) {
    if(transformNotSupported) {
        assert.expect(0);
        return;
    }

    assert.expect(1);

    var $scrollView = $("#scrollView").dxScrollView({
        useNative: true,
        refreshStrategy: "slideDown",
        onReachBottom: function() {
            assert.ok(true, "onReachBottom action was fired");
        }
    });

    var $container = $scrollView.find("." + SCROLLABLE_CONTAINER_CLASS);

    $container.scrollTop($container.prop("scrollHeight") - $container.prop("clientHeight"));
    $container.trigger("scroll");
});

QUnit.test("reach bottom element toggle", function(assert) {
    if(transformNotSupported) {
        assert.expect(0);
        return;
    }

    var $scrollView = $("#scrollView").dxScrollView({
        useNative: true,
        refreshStrategy: "slideDown"
    });

    var $reachBottom = $scrollView.find("." + SCROLLVIEW_BOTTOM_POCKET_CLASS);

    assert.ok($reachBottom.is(":hidden"), "reach bottom is hidden");

    $scrollView.dxScrollView("option", "onReachBottom", $.noop);

    assert.ok($reachBottom.is(":visible"), "reach bottom is hidden");
});

QUnit.test("release fires update", function(assert) {
    if(transformNotSupported) {
        assert.expect(0);
        return;
    }
    var updated = 0;

    var $scrollView = $("#scrollView").dxScrollView({
        useNative: true,
        onUpdated: function() {
            updated++;
        }
    });
    assert.equal(updated, 1, "update fired once after creation");
    var clock = sinon.useFakeTimers();
    try {
        $scrollView.dxScrollView("release");
        clock.tick(400);
        assert.equal(updated, 2, "update fired");

    } finally {
        clock.restore();
    }
});

QUnit.test("scroll fires with correctly arguments", function(assert) {
    if(transformNotSupported || this._originalPlatform === "android" && devices.real().version[0] < 4) {
        assert.expect(0);
        return;
    }

    assert.expect(12);

    var top = true,
        left = true,
        right = false,
        bottom = false;

    var $scrollView = $("#scrollView").width(50).height(50);
    $scrollView.children().width(100).height(100);
    $scrollView.dxScrollView({
        useNative: true,
        refreshStrategy: "slideDown",
        direction: "both",
        onScroll: function(e) {
            assert.equal(e.reachedTop, top, "reached top is correct");
            assert.equal(e.reachedLeft, left, "reached left is correct");
            assert.equal(e.reachedRight, right, "reached right is correct");
            assert.equal(e.reachedBottom, bottom, "reached bottom is correct");
        }
    });

    var scrollView = $scrollView.dxScrollView("instance");

    var $container = $("." + SCROLLABLE_CONTAINER_CLASS, $scrollView).on("scroll", function(e) {
        e.stopPropagation(); // NOTE: prevent bubbling of scroll event triggered with jQuery
    });

    $container.scrollTop(this.originalJQueryScrollTop.call($container)); // NOTE: update scrollTop position

    $container.trigger("scroll");

    scrollView.scrollTo({ x: 1, y: 1 });

    top = false; left = false;
    $container.trigger("scroll");
    scrollView.scrollTo({
        x: $container.prop("scrollWidth") - $container.prop("clientWidth"),
        y: $container.prop("scrollHeight") - $container.prop("clientHeight")
    });

    right = true;
    bottom = true;

    $container.trigger("scroll");
});

QUnit.module("regressions", moduleConfig);

QUnit.skip("B251572 - dxScrollView - Scroll position flies away when setting the direction option to horizontal or both", function(assert) {
    var $scrollView = $("#scrollView").dxScrollView({});

    $scrollView.dxScrollView("option", "direction", "horizontal");
    var location = getScrollOffset($scrollView);
    assert.equal(location.left, 0);
});

QUnit.test("B252260 - dxScrollView raise the 'Unknown dxScrollView refresh strategy' error in a simulator", function(assert) {
    assert.expect(0);
    $("#scrollView").dxScrollView({ useNativeScrolling: true }).dxScrollView("instance");
});


QUnit.module("default value", {
    beforeEach: function() {
        moduleConfig.beforeEach.call(this);
        this.originalRealDevice = devices.real();
        this.originalCurrentDevice = devices.current();
    },
    afterEach: function() {
        moduleConfig.afterEach.call(this);
        devices.real(this.originalRealDevice);
        devices.current(this.originalCurrentDevice);
    }
});

QUnit.test("refreshStrategy for ios set by real device", function(assert) {
    devices.real({ platform: "ios" });
    devices.current({ platform: "android" });

    var scrollView = $("#scrollView").dxScrollView().dxScrollView("instance");
    assert.equal(scrollView.option("refreshStrategy"), "pullDown");
});

QUnit.test("refreshStrategy for win8 set by real device", function(assert) {
    devices.real({ platform: "win" });
    devices.current({ platform: "android" });

    var scrollView = $("#scrollView").dxScrollView({ useNative: false }).dxScrollView("instance");

    assert.equal(scrollView.option("refreshStrategy"), "slideDown");

});

QUnit.test("refreshStrategy for android set by real device", function(assert) {
    devices.real({ platform: "android", version: "4" });
    devices.current({ platform: "ios" });

    var scrollView = $("#scrollView").dxScrollView().dxScrollView("instance");

    assert.equal(scrollView.option("refreshStrategy"), "swipeDown");
});

QUnit.test("B251645 dxList: Pull down to refresh is active in design time", function(assert) {
    config({ designMode: true });

    try {
        var pullRefreshActionCalled = 0,
            pageLoadingActionCalled = 0,

            scrollView = $("#scrollView").dxScrollView({
                onPullDown: function() {
                    pullRefreshActionCalled++;
                },
                onReachBottom: function() {
                    pageLoadingActionCalled++;
                }
            }).dxScrollView("instance");

        scrollView._pullDownAction();
        scrollView._reachBottomAction();

        assert.equal(pullRefreshActionCalled, 0);
        assert.equal(pageLoadingActionCalled, 0);

    } finally {
        config({ designMode: false });
    }
});


QUnit.module("pullDown and reachBottom events", moduleConfig);

QUnit.test("topPocket visibility depends on pullDown event", function(assert) {
    var $scrollView = $("#scrollView").dxScrollView({ useNative: false }),
        $topPocket = $scrollView.find("." + SCROLLVIEW_PULLDOWN_CLASS);

    $scrollView.dxScrollView("instance").on("pullDown", $.noop);

    assert.ok($topPocket.is(":visible"), "topPocket is visible");
});

QUnit.test("pullDown event should be fired after refresh method call", function(assert) {
    assert.expect(1);

    var $scrollView = $("#scrollView").dxScrollView({ useNative: false }),
        instance = $scrollView.dxScrollView("instance");

    instance.on("pullDown", function() {
        assert.ok(true, "pullDown is fired on refresh");
    });

    instance.refresh();
});

QUnit.test("bottomPocket element depends on reachBottom event", function(assert) {
    var $scrollView = $("#scrollView").dxScrollView({ useNative: false });

    $scrollView.dxScrollView("instance").on("reachBottom", $.noop);

    var $reachBottom = $scrollView.find("." + SCROLLVIEW_REACHBOTTOM_CLASS);

    assert.ok($reachBottom.is(":visible"), "reach bottom is visible");
});

QUnit.test("scrollview events support chains", function(assert) {
    var $scrollView = $("#scrollView").dxScrollView({ useNative: false });

    $scrollView.dxScrollView("instance").on("reachBottom", $.noop).on("pullDown", $.noop);

    assert.ok(true, "chains is supported");
});
